package minirestwebservice;

import java.io.PrintWriter;
import java.io.StringWriter;
import java.net.MalformedURLException;
import java.net.URL;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.logging.Level;

import org.olap4j.CellSet;
import org.olap4j.CellSetFormatterTest.Format;
import org.olap4j.OlapException;
import org.olap4j.driver.olap4ld.Olap4ldUtil;
import org.olap4j.driver.olap4ld.helper.Olap4ldLinkedDataUtil;
import org.olap4j.driver.olap4ld.linkeddata.BaseCubeOp;
import org.olap4j.driver.olap4ld.linkeddata.ConvertContextOp;
import org.olap4j.driver.olap4ld.linkeddata.ConvertContextSparqlIterator;
import org.olap4j.driver.olap4ld.linkeddata.EmbeddedSesameEngine;
import org.olap4j.driver.olap4ld.linkeddata.LinkedDataCubesEngine;
import org.olap4j.driver.olap4ld.linkeddata.LogicalOlapOp;
import org.olap4j.driver.olap4ld.linkeddata.LogicalOlapQueryPlan;
import org.olap4j.driver.olap4ld.linkeddata.PhysicalOlapQueryPlan;
import org.olap4j.driver.olap4ld.linkeddata.Restrictions;
import org.olap4j.layout.RectangularCellSetFormatter;
import org.olap4j.layout.TraditionalCellSetFormatter;
import org.semanticweb.yars.nx.Node;

public class Mioeur2eur {
	
	// URIs? 
	String DOMAINURI = "http://141.52.218.13:8080/QB-Slicer/rest/mioeur2eur?dsUri=";
	// The URI of the new dataset should resolve to:
	// http://141.52.218.13:8080/QB-Slicer/rest/mioeur2eur?dsUri=http%3A%2F%2Folap4ld.googlecode.com%2Fgit%2FOLAP4LD-trunk%2Ftests%2Fssb001%2Fttl%2Fexample.ttl%23ds
	
	private LinkedDataCubesEngine lde;
	// Stores members of dimension (dimensionUniquename hash code)
	private HashMap<Integer, List<Node[]>> membersofdimensions;
	private List<Node[]> members;
	private List<Node[]> hierarchies;
	private List<Node[]> levels;
	private List<Node[]> dimensions;
	private List<Node[]> measures;
	private List<Node[]> cubes;
	private LogicalOlapQueryPlan logicalquery;
	private PhysicalOlapQueryPlan physicalquery;

	/**
	 * Slicer slices one specific dataset
	 * 
	 * @param dsUri
	 * @throws OlapException
	 */
	public Mioeur2eur(String dsUri) throws OlapException {

		Olap4ldUtil.prepareLogging();
		// Logging
		// For debugging purposes
		Olap4ldUtil._log.setLevel(Level.CONFIG);

		// For monitoring usage
		//Olap4ldUtil._log.setLevel(Level.INFO);

		// For warnings (and errors) only
		// Olap4ldUtil._log.setLevel(Level.WARNING);

		try {
			
			// Must have settings without influence on query processing
			URL serverUrlObject = new URL("http://example.de");
			List<String> datastructuredefinitions = new ArrayList<String>();
			List<String> datasets = new ArrayList<String>();
			
			lde = new EmbeddedSesameEngine(serverUrlObject,
					datastructuredefinitions, datasets, "EMBEDDEDSESAME");
			
			populateMetadata(dsUri);
			LogicalOlapOp basecube = new BaseCubeOp(cubes,measures,dimensions,hierarchies,levels,members);
			LogicalOlapOp mioeur2eur = new ConvertContextOp(basecube, ConvertContextSparqlIterator.MIOEUR2EUR, DOMAINURI);
			LogicalOlapQueryPlan myplan = new LogicalOlapQueryPlan(
					mioeur2eur);
			executeStatement(myplan);

		} catch (MalformedURLException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		}
	}

	/**
	 * The question is, how do we return the dataset?
	 * 
	 * 
	 * 
	 * 
	 * 
	 * @return RDF of derived dataset.
	 */
	public String getResult() {

		ConvertContextSparqlIterator convertcontextiterator = (ConvertContextSparqlIterator) this.physicalquery.getIterator();
		
		// How to create the RDF?
		return convertcontextiterator.dumpRDF();
	}

	public LogicalOlapQueryPlan getLogicalQuery() {

		return logicalquery;
	}

	public PhysicalOlapQueryPlan getPhysicalQuery() {

		return physicalquery;
	}

	private void populateMetadata(String dsUri) {
		Restrictions restrictions = new Restrictions();
		restrictions.cubeNamePattern = dsUri;

		try {
			// In order to fill the engine with data
			this.cubes = lde.getCubes(restrictions);

			Map<String, Integer> cubemap = Olap4ldLinkedDataUtil
					.getNodeResultFields(cubes.get(0));
			System.out.println("CUBE_NAME: "
					+ cubes.get(1)[cubemap.get("?CUBE_NAME")]);

			// Get all metadata

			this.measures = lde.getMeasures(restrictions);

			System.out.println("MEASURE_UNIQUE_NAMES:");
			Map<String, Integer> measuremap = Olap4ldLinkedDataUtil
					.getNodeResultFields(measures.get(0));
			for (Node[] nodes : measures) {
				System.out
						.println(nodes[measuremap.get("?MEASURE_UNIQUE_NAME")]);
			}

			this.dimensions = lde.getDimensions(restrictions);

			System.out.println("DIMENSION_UNIQUE_NAMES:");
			Map<String, Integer> dimensionmap = Olap4ldLinkedDataUtil
					.getNodeResultFields(dimensions.get(0));
			for (Node[] nodes : dimensions) {
				System.out.println(nodes[dimensionmap
						.get("?DIMENSION_UNIQUE_NAME")]);
			}
			
			this.hierarchies = lde.getHierarchies(restrictions);
			
			this.levels = lde.getLevels(restrictions);
			
			// Collect members
			this.members = lde.getMembers(restrictions);
			this.membersofdimensions = new HashMap<Integer, List<Node[]>>();
			for (int i = 1; i < dimensions.size(); i++) {
				restrictions = new Restrictions();
				restrictions.cubeNamePattern = dsUri;
				restrictions.dimensionUniqueName = dimensions.get(i)[dimensionmap
						.get("?DIMENSION_UNIQUE_NAME")].toString();
				// Fix all members
				List<Node[]> members = lde.getMembers(restrictions);
				membersofdimensions.put(
						restrictions.dimensionUniqueName.hashCode(), members);

				System.out.println("DIMENSION_UNIQUE_NAME:"
						+ restrictions.dimensionUniqueName);
				System.out.println("MEMBER_UNIQUE_NAMES:");
				Map<String, Integer> membermap = Olap4ldLinkedDataUtil
						.getNodeResultFields(members.get(0));
				for (Node[] nodes : members) {
					System.out.println(nodes[membermap
							.get("?MEMBER_UNIQUE_NAME")]);
				}
			}

		} catch (OlapException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		}
	}
	
	private void executeStatement(LogicalOlapQueryPlan queryplan) {
		try {
			this.logicalquery = queryplan;
			// For now, we simply return plan
			System.out.println("--------------");
			System.out.println("Logical plan:" + queryplan.toString());
			// Execute query return representation of physical query plan
			List<Node[]> result = this.lde.executeOlapQuery(queryplan);

			this.physicalquery = this.lde.getExecplan(queryplan);
			System.out.println("Physical plan:" + physicalquery.toString());
			System.out.println("Result:");
			for (Node[] nodes : result) {
				for (Node node : nodes) {
					System.out.print(node.toString() + "; ");
				}
				System.out.println();
			}
			System.out.println("--------------");
			
		} catch (Exception e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		}
	}

	/**
	 * Converts a {@link CellSet} to text.
	 * 
	 * @param cellSet
	 *            Query result
	 * @param format
	 *            Format
	 * @return Result as text
	 */
	static String toString(CellSet cellSet, Format format) {
		StringWriter sw = new StringWriter();
		PrintWriter pw = new PrintWriter(sw);
		switch (format) {
		case TRADITIONAL:
			new TraditionalCellSetFormatter().format(cellSet, pw);
			break;
		case COMPACT_RECTANGULAR:
		case RECTANGULAR:
			new RectangularCellSetFormatter(
					format == Format.COMPACT_RECTANGULAR).format(cellSet, pw);
			break;
		}
		pw.flush();
		return sw.toString();
	}

}
